package com.hghivln.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.IRedisManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.DelegatingFilterProxy;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;


/**
 * shiro启用注解拦截控制器
 */
@Configuration
@EnableConfigurationProperties({ShiroProperties.class,RedisProperties.class})
public class ShiroConfig {

    @Autowired
    private ShiroProperties shiroProperties;

    @Autowired
    private RedisProperties redisProperties;

    /**
     * 声明凭证匹配器
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(shiroProperties.getHashAlgorithmName());
        credentialsMatcher.setHashIterations(shiroProperties.getHashIterations());
        return credentialsMatcher;
    }

    /**
     * 声明userRealm
     */
    @Bean
    public UserRealm userRealm(HashedCredentialsMatcher credentialsMatcher) {
        UserRealm userRealm = new UserRealm();
        // 注入凭证匹配器
        userRealm.setCredentialsMatcher(credentialsMatcher);
        return userRealm;
    }

    /**
     * 创建会话管理器
     */
    @Bean
    public TokenWebSessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
        TokenWebSessionManager sessionManager = new TokenWebSessionManager();
        // 注入sessionDao
        sessionManager.setSessionDAO(redisSessionDAO);
        sessionManager.setSessionIdCookieEnabled(false);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**
     * 创建安全管理器
     */
    @Bean
    public DefaultWebSecurityManager securityManager(UserRealm userRealm, TokenWebSessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    /**
     * 使用Redis 来存储登录的信息
     * sessionDao 还需要设置给sessionManager
     */
    @Bean
    public RedisSessionDAO redisSessionDAO(IRedisManager redisManager) {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        // 操作哪个redis
        redisSessionDAO.setRedisManager(redisManager);
        // 用户的登录信息保存多久？ 7天
        //redisSessionDAO.setExpire(7 * 24 * 3600);
        redisSessionDAO.setExpire(2 * 24 * 3600);
        return redisSessionDAO;
    }

    @Bean
    public IRedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 链接池的最量 20 ，并发特别大时，连接池的数据可以最大增加20个
        jedisPoolConfig.setMaxTotal(redisProperties.getJedis().getPool().getMaxActive());
        // 连接池的最大剩余量15个 ：并发不大，池里面的对象用不上，里面对象太多了。浪费空间
        jedisPoolConfig.setMaxIdle(redisProperties.getJedis().getPool().getMaxIdle());
        // 连接池初始就有10 个
        jedisPoolConfig.setMinIdle(redisProperties.getJedis().getPool().getMinIdle());
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, redisProperties.getHost(), redisProperties.getPort(), 2000, redisProperties.getPassword());
        redisManager.setJedisPool(jedisPool);
        return redisManager;
    }

    /**
     * 创建shiro拦截器 处理拦截资源文件问题
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager, ShiroFilterChainDefinition shiroFilterChainDefinition) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();

        // 创建自定义filter
        Map<String, Filter> map = new HashMap<>();
        map.put("authc", new LoginFilter());
        shiroFilter.setFilters(map);

        // 注入安全管理器
        shiroFilter.setSecurityManager(securityManager);
        Map<String, String> filterChainMap = shiroFilterChainDefinition.getFilterChainMap();
        shiroFilter.setFilterChainDefinitionMap(filterChainMap);
        return shiroFilter;

    }

     //shiro过滤器链的定义，定义哪一些链需要经过哪个 过滤器（默认）
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        // 设置放行的路径
        String[] anonUrls = shiroProperties.getAnonUrls();
        if (!StringUtils.isEmpty(anonUrls)) {
            for (String anonUrl : anonUrls) {
                map.put(anonUrl, "anon");
            }
        }
        // 设置登出路径
        String logOutUrl = shiroProperties.getLogOutUrl();
        if (!StringUtils.isEmpty(logOutUrl)) {
            map.put(logOutUrl, "logout");
        }
        // 设置拦截路径
        String[] authcUlrs = shiroProperties.getAuthcUrls();
        if (!StringUtils.isEmpty(authcUlrs)) {
            for (String authc : authcUlrs) {
                map.put(authc, "authc");
            }
        }
        chainDefinition.addPathDefinitions(map);
        return chainDefinition;
    }

    // 注册shiro的委托过滤器
    @Bean
    public FilterRegistrationBean<DelegatingFilterProxy> delegatingFilterProxy() {
        FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBean = new FilterRegistrationBean<>();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }

    // 开启shiro aop注解支持
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }


}
